#pragma once

#include "mq_exchange.hpp"
#include "mq_queue.hpp"
#include "mq_binding.hpp"
#include "mq_message.hpp"

#include <string>

namespace mq{   
    class VirtualHost
    {
    private:
        std::string _host_name;     //虚拟机名称
        ExchangeManager::ptr _emp;
        MsgQueueManager::ptr _mqmp;
        BindingManager::ptr _bmp;
        MessageManager::ptr _mmp;
    public:
    using ptr = std::shared_ptr<VirtualHost>;
        VirtualHost(const std::string &hname,const std::string &basedir,const std::string &dbfile)
            :_host_name(hname),
            _emp(std::make_shared<ExchangeManager>(dbfile)),
            _mqmp(std::make_shared<MsgQueueManager>(dbfile)),
            _bmp(std::make_shared<BindingManager>(dbfile)),
            _mmp(std::make_shared<MessageManager>(basedir)){
                //交换机，队列和绑定信息会在构造函数的时候进行数据恢复。
                //而队列消息没有在构造函数时进行数据恢复，因为我们的队列信息是按照队列为单元进行存储的,这个MessageManager是队列消息的总体管理类
                //获取当前已经存在的队列,为这些队列创建队列消息类，并进行数据恢复
                std::unordered_map<std::string,MsgQueue::ptr> qmap = _mqmp->AllQueue();
                for(auto &q : qmap){
                    _mmp->InitQueueMessage(q.first);    //进行数据恢复
                }
            }

        //交换机操作
        bool declareExchange(const std::string &name,
            ExchangeType type, bool durable, bool auto_delete,
            const google::protobuf::Map<std::string, std::string> &args){
            return _emp->declareExchange(name,type,durable,auto_delete,args);
        }
        void deleteExchange(const std::string &name){
            //删除交换机，同时需要删除交换机的所有绑定信息
           _bmp->removeExchangeBindngs(name);
           _emp->deleteExchange(name);
        }
        bool existsExchange(const std::string &name){
            return _emp->exists(name);
        }

        //队列的操作
        bool declareQueue(const std::string &name, bool durable,
                        bool exclusive, bool auto_delete,
                        const google::protobuf::Map<std::string, std::string> &args){
            //创建队列时，需要位队列创建队列消息管理
            _mmp->InitQueueMessage(name);
            return _mqmp->declareQueue(name,durable,exclusive,auto_delete,args);
        }
        void deleteQueue(const std::string &name){
            //删除队列，同时需要删除队列所有的绑定信息和队列消息
            _bmp->removeMsgQueueBindings(name);
            _mmp->DestoryQueueMessage(name);
            _mqmp->deleteQueue(name);
        }
        std::unordered_map<std::string, mq::MsgQueue::ptr> allQueue()
        {
            return _mqmp->AllQueue();
        }

        bool existsQueue(const std::string &name){
            return _mqmp->exists(name);
        }

        //绑定信息的操作
        void bind(const std::string &ename, const std::string &qname, const std::string &key){
            //需要判断队列和交换机的持久化标志位
            Exchange::ptr ecp = _emp->selectExchange(ename);
            if(ecp.get() == nullptr){
                ELOG("进行队列绑定失败,交换机%s不存在",ename.c_str());
                return;
            }
            MsgQueue::ptr mqp = _mqmp->selectQueue(qname);
            if(mqp.get() == nullptr){
                ELOG("进行队列绑定失败,队列%s不存在",qname.c_str());
                return;
            }

            //交换机和队列的持久化位必须同时满足，绑定消息的数据才可以持久化
            _bmp->bind(ename,qname,key,ecp->durable && mqp->durable);
        }
        void unbind(const std::string &ename, const std::string &qname){
            _bmp->unbind(ename,qname);
        }
        //获取交换机绑定的队列，在交换机收到消息后，需要向队列发送消息
        MsgQueueBindingMap getExchangeBindings(const std::string &ename){
            return _bmp->getExchangeBindings(ename);
        }
        bool existsBinding(const std::string &ename,const std::string &qname){
            return _bmp->exists(ename,qname);
        }

        Exchange::ptr selectExchange(const std::string &ename)
        {
            return _emp->selectExchange(ename);
        }

        //发布一条消息,只是做了一个存储
        bool basicPublish(const std::string &qname, BasicProperties *bp, std::string body){
            //需要判断队列的持久化标志位
            MsgQueue::ptr mqp = _mqmp->selectQueue(qname);
            if(mqp.get() == nullptr){
                ELOG("发布消息失败,队列%s不存在",qname.c_str());
                return false;
            }

            return _mmp->insert(qname,bp,body,mqp->durable);
        }
        //消费一条消息,只是把消息取出，由路由交换模块进行推送消息
        MessagePtr basicConsume(const std::string &qname){
            return _mmp->front(qname);
        }
        //确认一条消息,就是做删除
        void basicAck(const std::string &qname,const std::string &msg_id){
            _mmp->ack(qname,msg_id);
        }

        void clear() {
            _emp->clear();
            _mqmp->clear();
            _bmp->clear();
            _mmp->clear();
        } 
    };   
}